package dao

import (
	"context"
	"errors"
	"github.com/bwmarrin/snowflake"
	"go.mongodb.org/mongo-driver/bson"
	"go.mongodb.org/mongo-driver/mongo"
	"go.mongodb.org/mongo-driver/mongo/options"
	"time"
)

type MongoArticleDao struct {
	client *mongo.Client

	database *mongo.Database
	//制作库
	col *mongo.Collection
	//线上库
	liveCol *mongo.Collection
	node    *snowflake.Node
}

func NewMongoArticleDao(client *mongo.Client, node *snowflake.Node) *MongoArticleDao {
	db := client.Database("webook")
	return &MongoArticleDao{
		client:   client,
		database: db,
		col:      db.Collection("articles"),
		liveCol:  db.Collection("published_articles"),
		node:     node,
	}
}
func (m *MongoArticleDao) Upsert(ctx context.Context, art PublishedArticle) error {
	filter := bson.M{"id": art.Id}
	now := time.Now().UnixMilli()
	art.Utime = now

	//$setOnInsert和$set中的属性记录不能相同
	//使用$set、upsert 存在则更新，不存在则新增
	//使用$setOnInsert、upsert存在则不操作，不存在则新增
	//$set 用于更新，
	//$setOnInsert 用于控制在插入的时候引入额外字段

	_, err := m.liveCol.UpdateOne(ctx, filter, bson.D{bson.E{Key: "$set", Value: art},
		bson.E{Key: "$setOnInsert", Value: bson.E{"ctime", now}}}, options.Update().SetUpsert(true))
	return err
}
func (m *MongoArticleDao) Insert(ctx context.Context, art Article) (int64, error) {
	art.Id = m.node.Generate().Int64()
	now := time.Now().UnixMilli()
	art.Ctime = now
	art.Utime = now
	_, err := m.col.InsertOne(ctx, art)
	return art.Id, err
}

func (m *MongoArticleDao) UpdateById(ctx context.Context, art Article) error {
	now := time.Now().UnixMilli()
	filter := bson.M{"id": art.Id, "author_id": art.AuthorId}
	update := bson.M{"$set": bson.M{
		"title":   art.Title,
		"content": art.Content,
		"utime":   now,
		"status":  art.Status,
	}}
	res, err := m.col.UpdateOne(ctx, filter, update)
	if err != nil {
		return err
	}
	if res.ModifiedCount != 1 {
		return errors.New("更新失败")
	}
	return nil
}

func (m *MongoArticleDao) Sync(ctx context.Context, art Article) (int64, error) {
	// 创建会话
	session, _ := m.client.StartSession()
	defer session.EndSession(ctx)
	_, err := session.WithTransaction(ctx, func(ctx mongo.SessionContext) (interface{}, error) {
		var err error
		//操作线上库
		if art.Id > 0 {
			err = m.UpdateById(ctx, art)
		} else {
			art.Id, err = m.Insert(ctx, art)
		}
		if err != nil {
			return nil, err
		}
		return nil, m.Upsert(ctx, PublishedArticle(art))
	})
	if err != nil {
		return 0, err
	}
	return art.Id, nil
}

func (m *MongoArticleDao) SyncStatus(ctx context.Context, art Article) error {
	// 创建会话
	session, _ := m.client.StartSession()
	defer session.EndSession(ctx)
	_, err := session.WithTransaction(ctx, func(ctx mongo.SessionContext) (interface{}, error) {
		now := time.Now().UnixMilli()
		filter := bson.M{"id": art.Id, "author_id": art.AuthorId}
		update := bson.M{"$set": bson.M{
			"status": art.Status,
			"utime":  now,
		}}
		_, err := m.col.UpdateOne(ctx, filter, update)
		if err != nil {
			return nil, err
		}
		_, err = m.liveCol.UpdateOne(ctx, filter, update)
		if err != nil {
			return nil, err
		}
		return nil, nil
	})
	return err
}
